package data_management;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;

import XMLManager.XML_P;
import XMLManager.yXMLReader;
import XMLManager.yXMLWriter;
import android.os.Environment;
import android.util.Log;

public class PatternDataManager implements PatternDataManagerBase, DataFileManagementBase{
	public static String FILEPATH  = Environment.getExternalStorageDirectory() + "/" + XML_P.FILE_NAME;
	
	// for singleton
	private static PatternDataManager me;
	static{
		me = new PatternDataManager();
	}
	public static PatternDataManager getInstance(){
		return me;
	}
	// constructor
	private PatternDataManager(){
		boolean isOpened = open();
		Log.i("LOADING Pattern DATA:", "" + isOpened);
	}
	
	// destructor
	public void finalize(){
		boolean isClosed = close();
		Log.i("CLOSING Pattern DATA:", ""+isClosed);
	}
	
	// pattern list management
	private PItemList itemList = new PItemList();
	public PItemList getList(){ return itemList; }
	
	// temporary pattern set
	private PatternSet temp = new PatternSet();
	private HashedPatterns hp = new HashedPatterns();
	
	//////////////////// Handling temporary pattern set/////////////////////////

	// setters for data
	public void setProgramInfo(String info)		{temp.setProgramInfo(info); }
	public void setProgramType(int type)		{temp.setProgramType(type); }
	public void setPatternNick(String nick)		{temp.setPatternNick(nick); }
	public void setPatternUnit(PatternUnit unit){temp.setPatternUnit(unit); }
	public boolean setPatternUnit(PatternUnit unit, int sensitivity){
		setPatternUnit(unit);
		PatternSet target = match(temp, sensitivity);
		if(target != null){
			temp.setPatternUnit(null);
			return true;
		}
		else return false;
	}
	
	public PatternSet getPatternSet(){
		return temp;
	}
	// tools for data handling
	public void 	reset() 	{temp.reset();}
	public boolean 	canUpdate() {return temp.isCompleted();}
	public void		prepareNext(){ 
		temp = new PatternSet();
	}
	
	////////////////////////////////////////////////////////////////////////////
	
	///////////////// Operations for data management ///////////////////////////
	public PatternSet update() {
		PatternSet ret = insert(temp);
		save();
		return ret;
	}

	
	Comparator<PatternSet> comparator = new Comparator<PatternSet>(){
		public int compare(PatternSet p1, PatternSet p2) {
			if(p1.first() > p2.first()) return 1;
			return -1;
		}
	};
	
	public PatternSet insert(PatternSet item) {
		PatternList list = hp.get(temp.keyOfHash());
		
		// new list for hash map
		if(list == null)
			list = new PatternList();
		
		// add new pattern to PatternList
		list.add(temp);
		
		// sorting between list items
		Collections.sort(list, comparator);
		
		// inserting list to hashed Patterns
		hp.put(temp.keyOfHash(), list);
		
		// updating new pattern to PItem List
		Log.e("temp", ""+temp);
		PItem _newPItem = new PItem(itemList.size(), temp);
		itemList.add(_newPItem);
		
		// preparing next insertion
		PatternSet ret = temp;
		prepareNext();
		
		return ret;
	}

	public String delete(PatternSet item) {
		item.delete();
		return "deleted";
	}

	public TimeWords timeword(PatternUnit pattern){
		TimeWords word = new TimeWords();
		if(pattern.size()%2!=0) return null;
		for(int i=0; i<pattern.size()-1; i++){
			word.add(pattern.get(i+1) - pattern.get(i));
		}
		return word;
	}
	
	public PatternSet match(PatternSet item, int sensitivity) {
		// get the list that is matched with key of hashmap
		PatternList alist = hp.get(item.keyOfHash());

		// if cannot find the inserted pattern
		if(alist == null) return null;
		
		// getting pivot timewords
		PatternUnit unit = item.getPatternUnit();
		TimeWords pivotWords = timeword(unit);
		TimeWords tempWords;
		PatternSet curPatternSet;
		
		// comparing with all that is on alist
		for(int i=0; i<alist.size(); i++){
			boolean flag = true;
			
			// get a pattern set
			curPatternSet = alist.get(i);
			
			// if a pattern is deleted, step to the next
			if(curPatternSet.isDeleted()) continue;
			
			// comparing between pivot and temp
			tempWords = timeword(alist.get(i).getPatternUnit());
			for(int j=0; j < pivotWords.size(); j++){
				Long pivotWord = pivotWords.get(j);
				Long tempWord = tempWords.get(j);
				
				if(!(pivotWord - sensitivity <= tempWord 
						&& tempWord <= pivotWord + sensitivity)){
					flag = false;
				}
			}
			if(flag) return alist.get(i);
		}
		
		return null;
	}

	/////////////////////////////////////////////////////////////////////////
	
	
	/////////////// operations for file management //////////////////////////
	// �����ͱ���:
	// ����
	// ���� ���� ����
	// ���� ���� Ÿ��
	// ���� �г���
	// open DB
	public boolean open() {
		// open the xml file
		yXMLReader yread = new yXMLReader(XML_P.FILE_NAME);
		boolean isOpened = yread.open();
		if (!isOpened)
			return false;

		// for recognizing what event is occurred
		int event;

		// current XML tag and data(text)
		String curTag = "";
		String curText = "";
		String line;

		while ((event = yread.event()) != yread.END_DOCUMENT) {
			// dealing tag
			if (event == yread.START_TAG)
				curTag = yread.getName();
			else if (event == yread.END_TAG)
				curTag = "";

			Log.i("XML Reading", "current tag: " + curTag);

			// dealing with pattern set
			if (curTag.equals(XML_P.PATTERN_SET)) {
				// pattern nick
				line = yread.getAttr(XML_P.name);
				temp.setPatternNick(line);
			}

			// dealing with Pattern Unit
			else if (curTag.equals(XML_P.PATTERN_UNIT)) {
				// dealing when no text
				if (yread.event() != yread.TEXT)
					break;

				// pattern unit
				PatternUnit unit = new PatternUnit();
				line = yread.getText();
				Scanner sc2 = new Scanner(line);
				
				while (sc2.hasNext()) {
					String t = sc2.next();
					System.out.println(t);
					unit.add(Long.parseLong(t));
				}
				temp.setPatternUnit(unit);
			}

			// dealing with Execution
			else if (curTag.equals(XML_P.EXECUTION_INFO)) {
				// program type
				line = yread.getAttr(XML_P.type);
				int type = Integer.parseInt(line);
				temp.setProgramType(type);

				if (yread.event() != yread.TEXT)
					break;

				else if (!curTag.equals(XML_P.EXECUTION_INFO))
					break;

				// program info
				line = yread.getText();
				temp.setProgramInfo(line);
			}

			// insert
			if (canUpdate()) {
				Log.d("XML Reading", "Complete");
				Log.d("XML Reading", "patternUnit:" + temp.getPatternUnit());
				Log.d("XML Reading", "programType:" + temp.getProgramType());
				Log.d("XML Reading", "programInfo:" + temp.getProgramInfo());
				Log.d("XML Reading", "patternNick:" + temp.getPatternNick());
				
				update();
				reset();
			}
		}
		return true;
	}
	// close DB
	public boolean close(){
		yXMLWriter ywrite = new yXMLWriter(XML_P.ROOT, XML_P.FILE_NAME, XML_P.ENCODING_CODE);
		
		// get the hashed patterns list
		Collection<PatternList> out = hp.values();
		java.util.Iterator<PatternList> oit = out.iterator();
		
		// write all to XML file
		while(oit.hasNext()){
			ArrayList<PatternSet> t = oit.next();
			for(int i=0; i<t.size(); i++){
				// getting a pattern set on the list
				PatternSet tempPatternSet = t.get(i);
				
				// checking whether the pattern is deleted
				// if it is, step to next pattern
				if(tempPatternSet.isDeleted()){
					continue;
				}
				
				// getting pattern unit from the pattern set 
				PatternUnit pattern = tempPatternSet.getPatternUnit();
				
				// converting pattern unit array to string
				String s_pattern = "";
				for(int j=0; j<pattern.size(); j++)
					s_pattern += pattern.get(j) + " ";
				
				// writing informations to XML File
				ywrite.newPattern(
						tempPatternSet.nick(), 
						s_pattern, 
						tempPatternSet.type(), 
						tempPatternSet.packageName()
				);
			}
		}
		// writing xml file
		ywrite.close();
		
		return true;
	}

	@Override
	public boolean save() {
		return close();
	}
	////////////////////////////////////////////////////////////////////
}
